#include <iostream>
#include "utf8_output.h"
#include <type_traits>
#include <vector>
#include <string>

// 1. 基础示例：只接受算术类型的函数
template <class T>
typename std::enable_if<std::is_arithmetic<T>::value, T>::type foo(T t)
{
    return t;
}

// 2. 只接受指针类型的函数
template <typename T>
typename std::enable_if<std::is_pointer<T>::value, void>::type
print_pointer(T t)
{
    std::cout << "指针值: " << t << std::endl;
}

// 3. 使用 enable_if_t (C++14) 简化语法
template <typename T>
std::enable_if_t<std::is_integral<T>::value, bool>
is_even(T t)
{
    return (t % 2) == 0;
}

// 4. 使用enable_if实现函数重载
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, T>::type
get_abs(T x)
{
    std::cout << "浮点数版本" << std::endl;
    return x < 0 ? -x : x;
}

template <typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
get_abs(T x)
{
    std::cout << "整数版本" << std::endl;
    return x < 0 ? -x : x;
}

// 1. 复合条件：同时检查多个类型特征
template <typename T>
typename std::enable_if<
    std::is_arithmetic<T>::value &&
        !std::is_same<T, bool>::value,
    bool>::type
is_positive(T value)
{
    return value > 0;
}

// 2. 自定义类型特征
template <typename T, typename = void>
struct has_size_method : std::false_type
{
};

template <typename T>
struct has_size_method<T,
                       std::void_t<decltype(std::declval<T>().size())>> : std::true_type
{
};

// 3. 使用自定义类型特征的函数
template <typename Container>
typename std::enable_if<has_size_method<Container>::value, size_t>::type
get_container_size(const Container &c)
{
    return c.size();
}

// 4. 多重条件的类型特征
template <typename T>
struct is_valid_numeric_type
{
    static constexpr bool value =
        std::is_arithmetic<T>::value &&
        !std::is_same<T, bool>::value &&
        !std::is_same<T, char>::value;
};

// 5. 使用自定义类型特征简化enable_if
template <typename T>
typename std::enable_if<is_valid_numeric_type<T>::value, T>::type
safe_divide(T a, T b)
{
    if (b == 0)
        throw std::runtime_error("除数不能为零");
    return a / b;
}

// 6. 使用enable_if实现可变参数模板的特化
template <typename T, typename... Args>
typename std::enable_if<
    std::is_constructible<T, Args...>::value,
    std::unique_ptr<T>>::type
make_unique_if(Args &&...args)
{
    return std::make_unique<T>(std::forward<Args>(args)...);
}

// 7. 使用enable_if实现类型萃取
template <typename T>
using EnableIfString = typename std::enable_if<
    std::is_same<T, std::string>::value ||
    std::is_same<T, const char *>::value>::type;

template <typename T>
EnableIfString<T> print_only_string(T value)
{
    std::cout << "字符串: " << value << std::endl;
}

int main()
{
    // 原有的测试代码
    auto r = foo(1);
    std::cout << "foo(1) = " << r << std::endl;

    auto r1 = foo(1.2);
    std::cout << "foo(1.2) = " << r1 << std::endl;

    // 新增测试代码
    int *ptr = nullptr;
    print_pointer(ptr); // 正确：ptr是指针类型
    // print_pointer(42);   // 错误：不能编译，因为42不是指针类型

    std::cout << "is_even(42) = " << is_even(42) << std::endl;
    // is_even(3.14);  // 错误：不能编译，因为3.14不是整数类型

    // 测试函数重载
    std::cout << "get_abs(-42) = " << get_abs(-42) << std::endl;     // 调用整数版本
    std::cout << "get_abs(-3.14) = " << get_abs(-3.14) << std::endl; // 调用浮点数版本

    // 测试新增的功能
    std::cout << "\n=== 测试新功能 ===\n";

    // 测试复合条件
    std::cout << "is_positive(42) = " << is_positive(42) << std::endl;
    // is_positive(true);  // 错误：bool类型不允许

    // 测试容器大小检查
    std::vector<int> vec{1, 2, 3};
    std::cout << "vector size: " << get_container_size(vec) << std::endl;
    std::string str = "hello";
    std::cout << "string size: " << get_container_size(str) << std::endl;
    // get_container_size(42);  // 错误：int没有size()方法

    // 测试安全除法
    try
    {
        std::cout << "safe_divide(10, 2) = " << safe_divide(10, 2) << std::endl;
        // safe_divide(true, false);  // 错误：bool类型不允许
        // safe_divide(10, 0);  // 运行时错误：除数为零
    }
    catch (const std::exception &e)
    {
        std::cout << "错误: " << e.what() << std::endl;
    }

    // 测试字符串特化
    print_only_string("Hello");
    print_only_string(std::string("World"));
    // print_only_string(42);  // 错误：不是字符串类型

    return 0;
}
